if ( !IO_APIC_IRQ(i) )
d->arch.irq_pirq[i] = d->arch.pirq_irq[i] = i;
+ if ( is_hvm_domain(d) )
+ {
+ d->arch.pirq_emuirq = xmalloc_array(int, d->nr_pirqs);
+ d->arch.emuirq_pirq = xmalloc_array(int, nr_irqs);
+ if ( !d->arch.pirq_emuirq || !d->arch.emuirq_pirq )
+ goto fail;
+ for (i = 0; i < d->nr_pirqs; i++)
+ d->arch.pirq_emuirq[i] = IRQ_UNBOUND;
+ for (i = 0; i < nr_irqs; i++)
+ d->arch.emuirq_pirq[i] = IRQ_UNBOUND;
+ }
+
+
if ( (rc = iommu_domain_init(d)) != 0 )
goto fail;
vmce_destroy_msr(d);
xfree(d->arch.pirq_irq);
xfree(d->arch.irq_pirq);
+ xfree(d->arch.pirq_emuirq);
+ xfree(d->arch.emuirq_pirq);
free_xenheap_page(d->shared_info);
if ( paging_initialised )
paging_final_teardown(d);
free_xenheap_page(d->shared_info);
xfree(d->arch.pirq_irq);
xfree(d->arch.irq_pirq);
+ xfree(d->arch.pirq_emuirq);
+ xfree(d->arch.emuirq_pirq);
}
unsigned long pv_guest_cr4_fixup(const struct vcpu *v, unsigned long guest_cr4)
return rc;
}
+static long hvm_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
+{
+ switch ( cmd )
+ {
+ case PHYSDEVOP_map_pirq:
+ case PHYSDEVOP_unmap_pirq:
+ case PHYSDEVOP_eoi:
+ case PHYSDEVOP_irq_status_query:
+ return do_physdev_op(cmd, arg);
+ default:
+ return -ENOSYS;
+ }
+}
+
static long hvm_vcpu_op(
int cmd, int vcpuid, XEN_GUEST_HANDLE(void) arg)
{
[ __HYPERVISOR_memory_op ] = (hvm_hypercall_t *)hvm_memory_op,
[ __HYPERVISOR_grant_table_op ] = (hvm_hypercall_t *)hvm_grant_table_op,
[ __HYPERVISOR_vcpu_op ] = (hvm_hypercall_t *)hvm_vcpu_op,
+ [ __HYPERVISOR_physdev_op ] = (hvm_hypercall_t *)hvm_physdev_op,
HYPERCALL(xen_version),
HYPERCALL(event_channel_op),
HYPERCALL(sched_op),
return rc;
}
+static long hvm_physdev_op_compat32(
+ int cmd, XEN_GUEST_HANDLE(void) arg)
+{
+ switch ( cmd )
+ {
+ case PHYSDEVOP_map_pirq:
+ case PHYSDEVOP_unmap_pirq:
+ case PHYSDEVOP_eoi:
+ case PHYSDEVOP_irq_status_query:
+ return compat_physdev_op(cmd, arg);
+ break;
+ default:
+ return -ENOSYS;
+ break;
+ }
+}
+
static hvm_hypercall_t *hvm_hypercall64_table[NR_hypercalls] = {
[ __HYPERVISOR_memory_op ] = (hvm_hypercall_t *)hvm_memory_op,
[ __HYPERVISOR_grant_table_op ] = (hvm_hypercall_t *)hvm_grant_table_op,
[ __HYPERVISOR_vcpu_op ] = (hvm_hypercall_t *)hvm_vcpu_op,
+ [ __HYPERVISOR_physdev_op ] = (hvm_hypercall_t *)hvm_physdev_op,
HYPERCALL(xen_version),
HYPERCALL(event_channel_op),
HYPERCALL(sched_op),
[ __HYPERVISOR_memory_op ] = (hvm_hypercall_t *)hvm_memory_op_compat32,
[ __HYPERVISOR_grant_table_op ] = (hvm_hypercall_t *)hvm_grant_table_op_compat32,
[ __HYPERVISOR_vcpu_op ] = (hvm_hypercall_t *)hvm_vcpu_op_compat32,
+ [ __HYPERVISOR_physdev_op ] = (hvm_hypercall_t *)hvm_physdev_op_compat32,
HYPERCALL(xen_version),
HYPERCALL(event_channel_op),
HYPERCALL(sched_op),
#include <xen/types.h>
#include <xen/event.h>
#include <xen/sched.h>
+#include <xen/irq.h>
#include <asm/hvm/domain.h>
#include <asm/hvm/support.h>
+/* Must be called with hvm_domain->irq_lock hold */
+static void assert_irq(struct domain *d, unsigned ioapic_gsi, unsigned pic_irq)
+{
+ int pirq = domain_emuirq_to_pirq(d, ioapic_gsi);
+ if ( pirq != IRQ_UNBOUND )
+ {
+ send_guest_pirq(d, pirq);
+ return;
+ }
+ vioapic_irq_positive_edge(d, ioapic_gsi);
+ vpic_irq_positive_edge(d, pic_irq);
+}
+
+/* Must be called with hvm_domain->irq_lock hold */
+static void deassert_irq(struct domain *d, unsigned isa_irq)
+{
+ if ( domain_emuirq_to_pirq(d, isa_irq) != IRQ_UNBOUND )
+ vpic_irq_negative_edge(d, isa_irq);
+}
+
static void __hvm_pci_intx_assert(
struct domain *d, unsigned int device, unsigned int intx)
{
isa_irq = hvm_irq->pci_link.route[link];
if ( (hvm_irq->pci_link_assert_count[link]++ == 0) && isa_irq &&
(hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
- {
- vioapic_irq_positive_edge(d, isa_irq);
- vpic_irq_positive_edge(d, isa_irq);
- }
+ assert_irq(d, isa_irq, isa_irq);
}
void hvm_pci_intx_assert(
isa_irq = hvm_irq->pci_link.route[link];
if ( (--hvm_irq->pci_link_assert_count[link] == 0) && isa_irq &&
(--hvm_irq->gsi_assert_count[isa_irq] == 0) )
- vpic_irq_negative_edge(d, isa_irq);
+ deassert_irq(d, isa_irq);
}
void hvm_pci_intx_deassert(
if ( !__test_and_set_bit(isa_irq, &hvm_irq->isa_irq.i) &&
(hvm_irq->gsi_assert_count[gsi]++ == 0) )
- {
- vioapic_irq_positive_edge(d, gsi);
- vpic_irq_positive_edge(d, isa_irq);
- }
+ assert_irq(d, gsi, isa_irq);
spin_unlock(&d->arch.hvm_domain.irq_lock);
}
if ( __test_and_clear_bit(isa_irq, &hvm_irq->isa_irq.i) &&
(--hvm_irq->gsi_assert_count[gsi] == 0) )
- vpic_irq_negative_edge(d, isa_irq);
+ deassert_irq(d, isa_irq);
spin_unlock(&d->arch.hvm_domain.irq_lock);
}
{
for ( i = 16; i < nr_irqs_gsi; i++ )
if ( !d->arch.pirq_irq[i] )
- break;
+ {
+ if ( !is_hvm_domain(d) ||
+ d->arch.pirq_emuirq[i] == IRQ_UNBOUND )
+ break;
+ }
if ( i == nr_irqs_gsi )
return -ENOSPC;
}
{
for ( i = d->nr_pirqs - 1; i >= nr_irqs_gsi; i-- )
if ( !d->arch.pirq_irq[i] )
- break;
+ {
+ if ( !is_hvm_domain(d) ||
+ d->arch.pirq_emuirq[i] == IRQ_UNBOUND )
+ break;
+ }
if ( i < nr_irqs_gsi )
return -ENOSPC;
}
peoi[sp].ready = 1;
flush_ready_eoi();
}
+
+int map_domain_emuirq_pirq(struct domain *d, int pirq, int emuirq)
+{
+ int old_emuirq = IRQ_UNBOUND, old_pirq = IRQ_UNBOUND;
+
+ ASSERT(spin_is_locked(&d->event_lock));
+
+ if ( !is_hvm_domain(d) )
+ return -EINVAL;
+
+ if ( pirq < 0 || pirq >= d->nr_pirqs ||
+ emuirq == IRQ_UNBOUND || emuirq >= (int) nr_irqs )
+ {
+ dprintk(XENLOG_G_ERR, "dom%d: invalid pirq %d or emuirq %d\n",
+ d->domain_id, pirq, emuirq);
+ return -EINVAL;
+ }
+
+ old_emuirq = domain_pirq_to_emuirq(d, pirq);
+ if ( emuirq != IRQ_PT )
+ old_pirq = domain_emuirq_to_pirq(d, emuirq);
+
+ if ( (old_emuirq != IRQ_UNBOUND && (old_emuirq != emuirq) ) ||
+ (old_pirq != IRQ_UNBOUND && (old_pirq != pirq)) )
+ {
+ dprintk(XENLOG_G_WARNING, "dom%d: pirq %d or emuirq %d already mapped\n",
+ d->domain_id, pirq, emuirq);
+ return 0;
+ }
+
+ d->arch.pirq_emuirq[pirq] = emuirq;
+ /* do not store emuirq mappings for pt devices */
+ if ( emuirq != IRQ_PT )
+ d->arch.emuirq_pirq[emuirq] = pirq;
+
+ return 0;
+}
+
+int unmap_domain_pirq_emuirq(struct domain *d, int pirq)
+{
+ int emuirq, ret = 0;
+
+ if ( !is_hvm_domain(d) )
+ return -EINVAL;
+
+ if ( (pirq < 0) || (pirq >= d->nr_pirqs) )
+ return -EINVAL;
+
+ ASSERT(spin_is_locked(&d->event_lock));
+
+ emuirq = domain_pirq_to_emuirq(d, pirq);
+ if ( emuirq == IRQ_UNBOUND )
+ {
+ dprintk(XENLOG_G_ERR, "dom%d: pirq %d not mapped\n",
+ d->domain_id, pirq);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ d->arch.pirq_emuirq[pirq] = IRQ_UNBOUND;
+ d->arch.emuirq_pirq[emuirq] = IRQ_UNBOUND;
+
+ done:
+ return ret;
+}
+
+int hvm_domain_use_pirq(struct domain *d, int pirq)
+{
+ int emuirq;
+
+ if ( !is_hvm_domain(d) )
+ return 0;
+
+ emuirq = domain_pirq_to_emuirq(d, pirq);
+ if ( emuirq != IRQ_UNBOUND && d->pirq_to_evtchn[pirq] != 0 )
+ return 1;
+ else
+ return 0;
+}
ioapic_guest_write(
unsigned long physbase, unsigned int reg, u32 pval);
+static int physdev_hvm_map_pirq(
+ struct domain *d, struct physdev_map_pirq *map)
+{
+ int pirq, ret = 0;
+
+ spin_lock(&d->event_lock);
+ switch ( map->type )
+ {
+ case MAP_PIRQ_TYPE_GSI: {
+ struct hvm_irq_dpci *hvm_irq_dpci;
+ struct hvm_girq_dpci_mapping *girq;
+ uint32_t machine_gsi = 0;
+
+ /* find the machine gsi corresponding to the
+ * emulated gsi */
+ hvm_irq_dpci = domain_get_irq_dpci(d);
+ if ( hvm_irq_dpci )
+ {
+ list_for_each_entry ( girq,
+ &hvm_irq_dpci->girq[map->index],
+ list )
+ machine_gsi = girq->machine_gsi;
+ }
+ /* found one, this mean we are dealing with a pt device */
+ if ( machine_gsi )
+ {
+ map->index = domain_pirq_to_irq(d, machine_gsi);
+ pirq = machine_gsi;
+ ret = (pirq > 0) ? 0 : pirq;
+ }
+ /* we didn't find any, this means we are dealing
+ * with an emulated device */
+ else
+ {
+ pirq = map->pirq;
+ if ( pirq < 0 )
+ pirq = get_free_pirq(d, map->type, map->index);
+ ret = map_domain_emuirq_pirq(d, pirq, map->index);
+ }
+ map->pirq = pirq;
+ break;
+ }
+
+ default:
+ ret = -EINVAL;
+ dprintk(XENLOG_G_WARNING, "map type %d not supported yet\n", map->type);
+ break;
+ }
+
+ spin_unlock(&d->event_lock);
+ return ret;
+}
+
static int physdev_map_pirq(struct physdev_map_pirq *map)
{
struct domain *d;
if ( d == NULL )
return -ESRCH;
+ if ( map->domid == DOMID_SELF && is_hvm_domain(d) )
+ {
+ ret = physdev_hvm_map_pirq(d, map);
+ goto free_domain;
+ }
+
if ( !IS_PRIV_FOR(current->domain, d) )
{
ret = -EPERM;
/* Verify or get irq. */
switch ( map->type )
{
- case MAP_PIRQ_TYPE_GSI:
- if ( map->index < 0 || map->index >= nr_irqs_gsi )
- {
- dprintk(XENLOG_G_ERR, "dom%d: map invalid irq %d\n",
- d->domain_id, map->index);
- ret = -EINVAL;
- goto free_domain;
- }
-
- irq = domain_pirq_to_irq(current->domain, map->index);
- if ( !irq )
- {
- if ( IS_PRIV(current->domain) )
- irq = map->index;
- else {
- dprintk(XENLOG_G_ERR, "dom%d: map pirq with incorrect irq!\n",
- d->domain_id);
- ret = -EINVAL;
- goto free_domain;
- }
- }
- break;
-
- case MAP_PIRQ_TYPE_MSI:
- irq = map->index;
- if ( irq == -1 )
- irq = create_irq();
+ case MAP_PIRQ_TYPE_GSI:
+ if ( map->index < 0 || map->index >= nr_irqs_gsi )
+ {
+ dprintk(XENLOG_G_ERR, "dom%d: map invalid irq %d\n",
+ d->domain_id, map->index);
+ ret = -EINVAL;
+ goto free_domain;
+ }
- if ( irq < 0 || irq >= nr_irqs )
- {
- dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
+ irq = domain_pirq_to_irq(current->domain, map->index);
+ if ( !irq )
+ {
+ if ( IS_PRIV(current->domain) )
+ irq = map->index;
+ else {
+ dprintk(XENLOG_G_ERR, "dom%d: map pirq with incorrect irq!\n",
d->domain_id);
ret = -EINVAL;
goto free_domain;
}
+ }
+ break;
- _msi.bus = map->bus;
- _msi.devfn = map->devfn;
- _msi.entry_nr = map->entry_nr;
- _msi.table_base = map->table_base;
- _msi.irq = irq;
- map_data = &_msi;
- break;
+ case MAP_PIRQ_TYPE_MSI:
+ irq = map->index;
+ if ( irq == -1 )
+ irq = create_irq();
- default:
- dprintk(XENLOG_G_ERR, "dom%d: wrong map_pirq type %x\n",
- d->domain_id, map->type);
+ if ( irq < 0 || irq >= nr_irqs )
+ {
+ dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
+ d->domain_id);
ret = -EINVAL;
goto free_domain;
+ }
+
+ _msi.bus = map->bus;
+ _msi.devfn = map->devfn;
+ _msi.entry_nr = map->entry_nr;
+ _msi.table_base = map->table_base;
+ _msi.irq = irq;
+ map_data = &_msi;
+ break;
+
+ default:
+ dprintk(XENLOG_G_ERR, "dom%d: wrong map_pirq type %x\n",
+ d->domain_id, map->type);
+ ret = -EINVAL;
+ goto free_domain;
}
spin_lock(&pcidevs_lock);
if ( ret == 0 )
map->pirq = pirq;
-done:
+ if ( !ret && is_hvm_domain(d) )
+ map_domain_emuirq_pirq(d, pirq, IRQ_PT);
+
+ done:
spin_unlock(&d->event_lock);
spin_unlock(&pcidevs_lock);
if ( (ret != 0) && (map->type == MAP_PIRQ_TYPE_MSI) && (map->index == -1) )
destroy_irq(irq);
-free_domain:
+ free_domain:
rcu_unlock_domain(d);
return ret;
}
if ( d == NULL )
return -ESRCH;
+ if ( is_hvm_domain(d) )
+ {
+ spin_lock(&d->event_lock);
+ ret = unmap_domain_pirq_emuirq(d, unmap->pirq);
+ spin_unlock(&d->event_lock);
+ goto free_domain;
+ }
+
ret = -EPERM;
if ( !IS_PRIV_FOR(current->domain, d) )
goto free_domain;
spin_unlock(&d->event_lock);
spin_unlock(&pcidevs_lock);
-free_domain:
+ free_domain:
rcu_unlock_domain(d);
return ret;
}
break;
if ( v->domain->arch.pirq_eoi_map )
evtchn_unmask(v->domain->pirq_to_evtchn[eoi.irq]);
- ret = pirq_guest_eoi(v->domain, eoi.irq);
+ if ( !is_hvm_domain(v->domain) ||
+ domain_pirq_to_emuirq(v->domain, eoi.irq) == IRQ_PT )
+ ret = pirq_guest_eoi(v->domain, eoi.irq);
+ else
+ ret = 0;
break;
}
if ( (irq < 0) || (irq >= v->domain->nr_pirqs) )
break;
irq_status_query.flags = 0;
+ if ( is_hvm_domain(v->domain) &&
+ domain_pirq_to_emuirq(v->domain, irq) != IRQ_PT )
+ {
+ ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0;
+ break;
+ }
+
/*
* Even edge-triggered or message-based IRQs can need masking from
* time to time. If teh guest is not dynamically checking for this
break;
/* Vector is only used by hypervisor, and dom0 shouldn't
- touch it in its world, return irq_op.irq as the vecotr,
- and make this hypercall dummy, and also defer the vector
- allocation when dom0 tries to programe ioapic entry. */
+ touch it in its world, return irq_op.irq as the vecotr,
+ and make this hypercall dummy, and also defer the vector
+ allocation when dom0 tries to programe ioapic entry. */
irq_op.vector = irq_op.irq;
ret = 0;
if ( (pirq < 0) || (pirq >= d->nr_pirqs) )
return -EINVAL;
- if ( !irq_access_permitted(d, pirq) )
+ if ( !is_hvm_domain(d) && !irq_access_permitted(d, pirq) )
return -EPERM;
spin_lock(&d->event_lock);
chn = evtchn_from_port(d, port);
d->pirq_to_evtchn[pirq] = port;
- rc = pirq_guest_bind(v, pirq,
- !!(bind->flags & BIND_PIRQ__WILL_SHARE));
- if ( rc != 0 )
+ if ( !is_hvm_domain(d) )
{
- d->pirq_to_evtchn[pirq] = 0;
- goto out;
+ rc = pirq_guest_bind(
+ v, pirq, !!(bind->flags & BIND_PIRQ__WILL_SHARE));
+ if ( rc != 0 )
+ {
+ d->pirq_to_evtchn[pirq] = 0;
+ goto out;
+ }
}
chn->state = ECS_PIRQ;
break;
case ECS_PIRQ:
- pirq_guest_unbind(d1, chn1->u.pirq.irq);
+ if ( !is_hvm_domain(d1) )
+ pirq_guest_unbind(d1, chn1->u.pirq.irq);
d1->pirq_to_evtchn[chn1->u.pirq.irq] = 0;
unlink_pirq_port(chn1, d1->vcpu[chn1->notify_vcpu_id]);
break;
struct evtchn *chn;
/*
- * It should not be possible to race with __evtchn_close():
- * The caller of this function must synchronise with pirq_guest_unbind().
+ * PV guests: It should not be possible to race with __evtchn_close(). The
+ * caller of this function must synchronise with pirq_guest_unbind().
+ * HVM guests: Port is legitimately zero when the guest disables the
+ * emulated interrupt/evtchn.
*/
- ASSERT(port != 0);
+ if ( port == 0 )
+ {
+ BUG_ON(!is_hvm_domain(d));
+ return 0;
+ }
chn = evtchn_from_port(d, port);
return evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
(1U << XENFEAT_gnttab_map_avail_bits);
else
fi.submap |= (1U << XENFEAT_hvm_safe_pvclock) |
- (1U << XENFEAT_hvm_callback_vector);
+ (1U << XENFEAT_hvm_callback_vector) |
+ (1U << XENFEAT_hvm_pirqs);
#endif
break;
default:
hvm_irq_dpci->mirq[machine_gsi].dom = NULL;
hvm_irq_dpci->mirq[machine_gsi].flags = 0;
clear_bit(machine_gsi, hvm_irq_dpci->mapping);
+ unmap_domain_pirq_emuirq(d, machine_gsi);
}
}
spin_unlock(&d->event_lock);
extern int vmsi_deliver(struct domain *d, int pirq);
static int hvm_pci_msi_assert(struct domain *d, int pirq)
{
- return vmsi_deliver(d, pirq);
+ if ( hvm_domain_use_pirq(d, pirq) )
+ return send_guest_pirq(d, pirq);
+ else
+ return vmsi_deliver(d, pirq);
}
#endif
{
device = digl->device;
intx = digl->intx;
- hvm_pci_intx_assert(d, device, intx);
+ if ( hvm_domain_use_pirq(d, pirq) )
+ send_guest_pirq(d, pirq);
+ else
+ hvm_pci_intx_assert(d, device, intx);
hvm_irq_dpci->mirq[pirq].pending++;
#ifdef SUPPORT_MSI_REMAPPING
/* NB. protected by d->event_lock and by irq_desc[irq].lock */
int *irq_pirq;
int *pirq_irq;
+ /* pirq to emulated irq and vice versa */
+ int *emuirq_pirq;
+ int *pirq_emuirq;
/* Shared page for notifying that explicit PIRQ EOI is required. */
unsigned long *pirq_eoi_map;
int unmap_domain_pirq(struct domain *d, int pirq);
int get_free_pirq(struct domain *d, int type, int index);
void free_domain_pirqs(struct domain *d);
+int map_domain_emuirq_pirq(struct domain *d, int pirq, int irq);
+int unmap_domain_pirq_emuirq(struct domain *d, int pirq);
+int hvm_domain_use_pirq(struct domain *d, int irq);
int init_irq_data(void);
#define domain_pirq_to_irq(d, pirq) ((d)->arch.pirq_irq[pirq])
#define domain_irq_to_pirq(d, irq) ((d)->arch.irq_pirq[irq])
+#define domain_pirq_to_emuirq(d, pirq) ((d)->arch.pirq_emuirq[pirq])
+#define domain_emuirq_to_pirq(d, emuirq) ((d)->arch.emuirq_pirq[emuirq])
+#define IRQ_UNBOUND -1
+#define IRQ_PT -2
bool_t cpu_has_pending_apic_eoi(void);
/* x86: pvclock algorithm is safe to use on HVM */
#define XENFEAT_hvm_safe_pvclock 9
+/* x86: pirq can be used by HVM guests */
+#define XENFEAT_hvm_pirqs 10
+
#define XENFEAT_NR_SUBMAPS 1
#endif /* __XEN_PUBLIC_FEATURES_H__ */